#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>

#include <unistd.h>

#define DEFAULT_PORT 8848       // 服务器默认公开端口

#define LISTEN_QUEUE 1024       // 连接的最大数量

#define MSG_SIZE 1024           // 接收端 获取数据的最大长度

int g_epfd = 0;         // 全局 epoll 套接字
int g_listenfd = 0;     // 服务端监听套接字

int util_socket_listen(const int _port);
int util_socket_accept(int _sockfd);
void util_socket_get_info(int _sockfd);


int util_epoll_create();
int utils_epoll_wait_loop(const int _epfd, const int _listenfd ,const int _loopNum);
int utils_epoll_add_fd(int _epfd, int _sockfd, bool enable_et);


int service_request(int _sockfd);
int service_response(int _sockfd);

int exec_client_read(int _sockfd);
int exec_client_close(int _epfd, int __sockfd);


int get_line(int sock, char *buf, int size);
void not_found(int client);


int main(int argc, char** argv)
{
    g_epfd = util_epoll_create();

    g_listenfd = util_socket_listen(DEFAULT_PORT); // 8848

    // listen套接字放入 epfd 中, 用于监听客户端连接事件
    utils_epoll_add_fd(g_epfd, g_listenfd, 0);

    utils_epoll_wait_loop(g_epfd, g_listenfd, -1); // debug : loop counts, -1 == forever
    // 客户端套接字

    close(g_listenfd);
    close(g_epfd);
}


// create socket bind listen
int util_socket_listen(const int _port) {

    int sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    struct sockaddr_in     servaddr;

    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(_port);

    //将本地地址绑定到所创建的套接字上
    if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind:(errno:%d %s)\n",errno,strerror(errno));
        return -1;
    }

    //开始监听是否有客户端连接
    if (listen(sockfd, LISTEN_QUEUE) == -1) {
        printf("listen:(errno:%d %s)\n",errno,strerror(errno));
        return -1;
    }

    return sockfd;
}

int util_socket_accept(int _sockfd) {
    struct sockaddr_in cliaddr;
    socklen_t clilen = sizeof(cliaddr);

    int cliFd  = accept(_sockfd, (struct sockaddr*)&cliaddr, &clilen);
    if (cliFd  == -1) {
        printf("accept: (errno: %d, %s)\n",errno, strerror(errno));
    }

    return cliFd;
}

void util_socket_get_info(int _sockfd){
    int sndbuf, rcvbuf;
    socklen_t optlen = sizeof(int);

    // 获取发送缓冲区的最大容量和当前可用容量
    if (getsockopt(_sockfd, SOL_SOCKET, SO_SNDBUF , &sndbuf, &optlen) == -1) {
        perror("getsockopt");
    }

    // 获取接收缓冲区的最大容量和当前可用容量
    if (getsockopt(_sockfd, SOL_SOCKET, SO_RCVBUF , &rcvbuf, &optlen) == -1) {
        perror("getsockopt");
    }

    printf("Send buffer max size: %d\n", sndbuf);
    printf("Receive buffer max size: %d\n", rcvbuf);

}

int util_epoll_create(){
    int epfd = epoll_create(1024);  // 1024 占位参数, 兼容旧协议, 实际在内核中会动态扩容
    if(epfd == -1){
        printf("epoll_create() err:%s\n", strerror(errno));
        return -1;
    }
    return epfd;
}

int utils_epoll_wait_loop(const int _epfd, const int _listenfd ,const int _loopNum){

    int ret = 0;

    int loopNum = _loopNum;

    struct epoll_event ep_revents[1024];

    while(loopNum--){
        int nrevent = epoll_wait(_epfd, ep_revents, 1024, -1);
        // printf("nrevent: %d\n",nrevent);
        if(nrevent == -1){
            printf("epoll_wait() err:%s\n", strerror(errno));
            ret = -1;
            break;
        }

        for(int i = 0; i < nrevent; ++i){

            int sockfd = ep_revents[i].data.fd;

            int ne = ep_revents[i].events;
            //printf("ne: %d\n", ne);

            // listenfd 的事件, 一般只是新客户端连接
            if(sockfd == _listenfd){
                int cliFd = util_socket_accept(ep_revents[i].data.fd);
                if(cliFd > 0){
                    utils_epoll_add_fd(_epfd, cliFd, 0);
                }
            }else if(ne & EPOLLIN){
                service_request(sockfd);
            }else if(ne & EPOLLRDHUP){
                printf("client hup: %d\n", sockfd);
                //exec_client_close(g_epfd, sockfd);
            } else{
                printf("something else happened \n");
            }

        }

    }

    return 0;
}


/* 将文件描述符fd上的EPOLLIN注册到g_epfd指示的epoll内核事件表中，
参数enable_et指定是否对fd启用ET模式 */
int utils_epoll_add_fd(int _epfd, int _sockfd, bool enable_et)
{
    struct epoll_event event;
    event.data.fd = _sockfd;
    event.events = EPOLLIN | EPOLLRDHUP;
    if (enable_et) {
            event.events |= EPOLLET; // 注意: 这里的.events 添加了EPOLLET标识, 表示对该事件启动ET模式
    }
    epoll_ctl(_epfd, EPOLL_CTL_ADD, _sockfd, &event);
    //setnonblocking(fd);  // 设置套接字为非阻塞模式
    return 0;
}

int service_request(int _sockfd){
    char buff[MSG_SIZE];
    memset(buff, 0, MSG_SIZE);
    //printf("exec_client_read():\n");

    int n = recv(_sockfd, buff, MSG_SIZE, 0);

    printf("%s\n",buff);

    not_found(_sockfd);

    //  do something to buff

    service_response(_sockfd);

    return 0;
}
int service_response(int _sockfd){

    close(_sockfd);
    return 0;
}

int exec_client_read(int _sockfd){

    return 0;
}

int exec_client_close(int _epfd, int _sockfd){
    epoll_ctl(_epfd, EPOLL_CTL_DEL, _sockfd, NULL);
    close(_sockfd);
    return 0;
}

int get_line(int sock, char *buf, int size)
{
    int i = 0;
    char c = '\0';
    int n;
    while ((i < size - 1) && (c != '\n'))
    {
        n = recv(sock, &c, 1, 0);
        /* DEBUG printf("%02X\n", c); */
        if (n > 0)
        {
            if (c == '\r')
            {
                n = recv(sock, &c, 1, MSG_PEEK);    // MSG_PEEK 只查看数据, 不移除数据

                /* DEBUG printf("%02X\n", c); */

                if ((n > 0) && (c == '\n'))
                    recv(sock, &c, 1, 0);
                else
                    c = '\n';
            }

            buf[i] = c;
            i++;
        }

        else
            c = '\n';
    }

    buf[i] = '\0';

    return(i);
}

void not_found(int client)
{
    char buf[1024];

    sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "your request because the resource specified\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "is unavailable or nonexistent.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
}
